#include <sys/socket.h>
#include <string>
#include <string.h>
#include "json.h"

static bool check_char_s(void *__fd, ssize_t &size, char &c, const char expect[]) {
    if (size <= 0) return false;
    for (;;) {
        if (recv((long)__fd, &c, 1, MSG_PEEK) != 1) return false;
        if (c == ' ' || c == '\t' || c == '\r' || c == '\n') {
            recv((long)__fd, &c, 1, 0);
            size--;
            continue;
        }
        if (expect == nullptr) break;
        bool match = false;
        for (int i = 0;expect[i] != 0;i++) {
            if (c == expect[i]) {
                match = true;
                break;
            }
        }
        if (match) break;
        return false;
    }
    return true;
}

static bool read_char_s(void *__fd, ssize_t &size, char &c) {
    if (size <= 0) return false;
    for (;;) {
        if (recv((long)__fd, &c, 1, 0) != 1) return false;
        size--;
        if (c == ' ' || c == '\t' || c == '\r' || c == '\n') continue;
        break;
    }
    return true;
}

static bool check_char_f(void *__fd, ssize_t &size, char &c, const char expect[]) {
    FILE *fd = (FILE *)__fd;
    if (size <= 0) return false;
    for (;size > 0;) {
        if (fread(&c, 1, 1, fd) != 1) return false;
        fseek(fd, -1, SEEK_CUR);
        if (c == ' ' || c == '\t' || c == '\r' || c == '\n') {
            fread(&c, 1, 1, fd);
            size--;
            continue;
        }
        if (expect == nullptr) break;
        bool match = false;
        for (int i = 0;expect[i] != 0;i++) {
            if (c == expect[i]) {
                match = true;
                break;
            }
        }
        if (match) break;
        return false;
    }
    return true;
}

static bool read_char_f(void *__fd, ssize_t &size, char &c) {
    FILE *fd = (FILE *)__fd;
    if (size <= 0) return false;
    for (;;) {
        if (fread(&c, 1, 1, fd) != 1) return false;
        size--;
        if (c == ' ' || c == '\t' || c == '\r' || c == '\n') continue;
        break;
    }
    return true;
}

void json_t::clear() {
    type = UNDEF;
    item_key.clear();
    item.clear();
    value_string.clear();
    value_num = 0;
    value_array.clear();
}

bool json_t::load(std::string fn) {
    FILE *f = fopen(fn.c_str(), "rb");
    fseek(f, 0, SEEK_END);
    ssize_t size = ftell(f);
    fseek(f, 0, SEEK_SET);

    return l(f, size, check_char_f, read_char_f);
}

bool json_t::sock_recv(int __fd, ssize_t &size) {
    return l((void *)(long)__fd, size, check_char_s, read_char_s);
}

bool json_t::l(void * __fd, ssize_t &size, bool (*check_char)(void * __fd, ssize_t &size, char &c, const char expect[]), bool (*read_char)(void * __fd, ssize_t &size, char &c)) {
    valid = true;
    char c;
    int minus = 1;
    std::string key;
    json_t value;

    clear();
    if (check_char(__fd, size, c, "{-+0123456789[\"ntf") == false) return false;
    if (c == '{') type = OBJ;
    else if (c == '\"') type = STRING;
    else if (c == '-' || c == '+' || (c >= '0' && c <= '9')) type = NUM;
    else if (c == '[') type = ARRAY;
    else if (c == 'n') type = NULLVALUE;
    else if (c == 't' || c == 'f') type = BOOL;
    else return false;

    switch (type) {
        case OBJ:
            read_char(__fd, size, c);//consume '{'
            for (;;) {
                if (check_char(__fd, size, c, "}")) {
                    read_char(__fd, size, c);//consume '}'
                    return true;
                }
                //read key
                if (read_char(__fd, size, c) == false) return false;
                if (c != '"') return false;
                key.clear();
                while (read_char(__fd, size, c)) {
                    if (c == '"') break;
                    key.push_back(c);
                    if (size == 0) return false;
                }

                if (read_char(__fd, size, c) == false || c != ':') return false;
                if (value.l(__fd, size, check_char, read_char) == false) return false;
                item_key.push_back(key);
                item[key] = value;
                if (read_char(__fd, size, c) == false) return false;
                if (c == '}') return true;
                if (c != ',') return false;
            }
            break;
        case STRING:
            read_char(__fd, size, c);//consume '"'
            value_string.clear();
            while (read_char(__fd, size, c)) {
                if (c == '\"' && value_string.back() != '\\') return true;
                value_string.push_back(c);
                if (size == 0) break;
            }
            break;
        case NUM:
            if (c == '-') minus = -1;
            if (c == '-' || c == '+') {
                read_char(__fd, size, c);//consume '- or +'
            }
            
            for (;size > 0;) {
                check_char(__fd, size, c, nullptr);
                if (c >= '0' && c <= '9') {
                    value_num = value_num * 10 + (c - '0');
                    read_char(__fd, size, c);
                } else {
                    value_num = value_num * minus;
                    return true;
                }
            }
            break;
        case BOOL:
            if (c == 't') {//true
                if (read_char(__fd, size, c) == false || c != 't') return false;
                if (read_char(__fd, size, c) == false || c != 'r') return false;
                if (read_char(__fd, size, c) == false || c != 'u') return false;
                if (read_char(__fd, size, c) == false || c != 'e') return false;
                value_num = 1;
                return true;
            } else if (c == 'f') {//false
                if (read_char(__fd, size, c) == false || c != 'f') return false;
                if (read_char(__fd, size, c) == false || c != 'a') return false;
                if (read_char(__fd, size, c) == false || c != 'l') return false;
                if (read_char(__fd, size, c) == false || c != 's') return false;
                if (read_char(__fd, size, c) == false || c != 'e') return false;
                value_num = 0;
                return true;
            } 
            break;
        case NULLVALUE:
            if (c == 'n') {//null
                if (read_char(__fd, size, c) == false || c != 'n') return false;
                if (read_char(__fd, size, c) == false || c != 'u') return false;
                if (read_char(__fd, size, c) == false || c != 'l') return false;
                if (read_char(__fd, size, c) == false || c != 'l') return false;
                value_num = 0;
                return true;
            }
            break;
        case ARRAY:
            value_array.clear();
            read_char(__fd, size, c);//consume '['
            while (value.l(__fd, size, check_char, read_char)) {
                value_array.push_back(value);
                if (check_char(__fd, size, c, ",]") == false) return false;
                if (c == ',') {
                    read_char(__fd, size, c);//consume ','
                } else {
                    break;
                }
            }
            if (read_char(__fd, size, c) == false || c != ']') return false;
            return true;
            break;
        default:
            break;
    }
    return false;
}

json_t &json_t::operator[](const char *key){
    if (this->type == UNDEF) {
        this->type = OBJ;
    }
    if (this->type != OBJ) {
        return *(json_t *)0;
    }
    std::string k(key);
    if (item.find(k) != item.end()) {
        return item[key];
    } else {
        item_key.push_back(key);
        item[key].type = UNDEF;
        return item[key];
    }
}

json_t &json_t::operator[](const std::string key) {
    return operator[](key.c_str());
}

json_t &json_t::operator[](size_t i) {
    if (type == OBJ) {
        return item[item_key[i]];
    } else if (type == ARRAY) {
        return value_array[i];
    }

    static json_t ret;
    return ret;
}

std::string json_t::key(int i) {
    if (i < (int)item_key.size()) {
        return item_key.at(i);
    }

    return "";
}

size_t json_t::size() {
    if (type == OBJ) {
        return item_key.size();
    } else if (type == ARRAY) {
        return value_array.size();
    }
    return 0;
}

bool json_t::is_valid() {
    return valid;
}

json_t::type_t json_t::gettype() {
    return type;
}

std::string &json_t::string() {
    return value_string;
}

int json_t::num() {
    return value_num;
}

bool json_t::b() {
    return (bool)value_num;
}

std::vector<json_t> &json_t::array() {
    return value_array;
}

void print_json(json_t &json, int indent, bool doindent) {
    if (doindent) for (int j = 0;j < indent;j++) printf("    ");
    std::string key;
    int n;
    switch (json.gettype()) {
        case json_t::STRING:
            printf("\"%s\"", json.string().c_str());
            break;
        case json_t::NUM:
            printf("%d", json.num());
            break;
        case json_t::BOOL:
            if (json.b()) {
                printf("true");
            } else {
                printf("false");
            }
            break;
        case json_t::NULLVALUE:
            printf("null");
            break;
        case json_t::OBJ:
            printf("{");
            for (int i = 0;;i++) {
                key = json.key(i);
                if (key == "") break;
                if (i != 0) printf(",\n");
                else printf("\n");
                for (int j = 0;j < indent + 1;j++) printf("    ");
                printf("\"%s\"", key.c_str());
                printf(": ");
                print_json(json[key], indent + 1, false);
            }
            printf("\n");
            for (int j = 0;j < indent;j++) printf("    ");
            printf("}");
            break;
        case json_t::ARRAY:
            printf("[");
            for (size_t i = 0;i < json.array().size();i++) {
                if (i > 0) printf(",");
                printf("\n");
                print_json(json.array()[i], indent + 1, true);
            }
            if (json.array().size() > 0) {
                printf("\n");
                for (int j = 0;j < indent;j++) printf("    ");
            }
            printf("]");
            break;
        default:
            break;
    }
    if (indent == 0) printf("\n");
}

bool json_t::append_array(json_t &value) {
    this->type = ARRAY;
    value_array.push_back(value);
    return true;
}

bool json_t::append_array(int value) {
    json_t n;
    n.clear();
    n = value;
    return append_array(n);
}

json_t &json_t::append_array() {
    json_t n;
    n.clear();
    append_array(n);
    return value_array.back();
}

bool json_t::append_array(std::string value) {
    json_t n;
    n.clear();
    n = value;
    return append_array(n);
}

int json_t::operator = (int value) {
    this->type = NUM;
    return (this->value_num = value);
}

std::string json_t::operator = (std::string value) {
    this->type = STRING;
    return (this->value_string = value);
}

const char *json_t::operator = (const char *value) {
    this->type = STRING;
    return (this->value_string = value).c_str();
}

void json_t::seek(long offset, int whence) {
    readp = 0;
}

static bool put_data(char *buf, char *tbuf, int n, long &p, long size) {
    if (p + n <= 0) {
        p += n;
        return true;
    } else {
        if (p < 0) {
            n = n + p;
            p = 0;
        }
    }
    if (p + n > size) n = size - p;
    memcpy(buf + p, tbuf, n);
    p += n;
    return (p + n < size);
}

long json_t::_read(char *buf, long &p, long size) {
    char tbuf[1024];
    int n;

    switch (type) {
        case json_t::STRING:
            n = sprintf(tbuf, "\"%s\"", string().c_str());
            if (put_data(buf, tbuf, n, p, size) == false) return p;
            break;
        case json_t::NUM:
            n = sprintf(tbuf, "%d", num());
            if (put_data(buf, tbuf, n, p, size) == false) return p;
            break;
        case json_t::BOOL:
            if (b()) {
                n = sprintf(tbuf, "true");
            } else {
                n = sprintf(tbuf, "false");
            }
            if (put_data(buf, tbuf, n, p, size) == false) return p;
            break;
        case json_t::NULLVALUE:
            n = sprintf(tbuf, "null");
            if (put_data(buf, tbuf, n, p, size) == false) return p;
            break;
        case json_t::OBJ:
            n = sprintf(tbuf, "{");
            if (put_data(buf, tbuf, n, p, size) == false) return p;
            for (auto &key : this->item_key) {
                if (&key != &this->item_key[0]) {
                    n = sprintf(tbuf, ",");
                    if (put_data(buf, tbuf, n, p, size) == false) return p;
                }
                n = sprintf(tbuf, "\"%s\"", key.c_str());
                if (put_data(buf, tbuf, n, p, size) == false) return p;
                n = sprintf(tbuf, ": ");
                if (put_data(buf, tbuf, n, p, size) == false) return p;
                item[key]._read(buf, p, size);
                if (p >= size) return p;
            }
            n = sprintf(tbuf, "}");
            if (put_data(buf, tbuf, n, p, size) == false) return p;
            break;
        case json_t::ARRAY:
            n = sprintf(tbuf, "[");
            if (put_data(buf, tbuf, n, p, size) == false) return p;
            for (size_t i = 0;i < array().size();i++) {
                if (i > 0) {
                    n = sprintf(tbuf, ",");
                    if (put_data(buf, tbuf, n, p, size) == false) return p;
                }
                array()[i]._read(buf, p, size);
                if (p >= size) return p;
            }
            n = sprintf(tbuf, "]");
            if (put_data(buf, tbuf, n, p, size) == false) return p;
            break;
        default:
            break;
    }
    return p;
}

long json_t::read(char *buf, long size) {
    long p = -readp;
    return _read(buf, p, size);
}
